﻿using System;
using System.IO;
using System.Linq;
using System.Threading;
using Microsoft.Kinect;
using Microsoft.Speech.AudioFormat;
using Microsoft.Speech.Recognition;

// source derived from the Kinect Tooblox
// http://kinecttoolbox.codeplex.com/
// By deltakosh and friends...great library, I just needed more flexability for adding/removing grammers dynamically

namespace Kinect.Toolbox.Voice
{
    /// <summary>
    /// Allows for simple voice commands which result in events being called
    /// </summary>
    public class VoiceCommander
    {
        Thread workingThread;
        readonly Choices choices;
        bool isRunning;
        SpeechRecognitionEngine speechRecognitionEngine;
        private KinectSensor kinectSensor;

        public event Action<string> OrderDetected;

        /// <summary>
        /// Instantiates a new instance
        /// </summary>
        /// <param name="sensor">Kinect Sensor which should listen for the commands</param>
        public VoiceCommander(KinectSensor sensor)
        {
            choices = new Choices();
            choices.Add(new string[] { "Computer" }); // this can't be blank. We give it a basic, simple command to start
            kinectSensor = sensor;

        }

        /// <summary>
        /// Adds orders to listen for
        /// </summary>
        /// <param name="orders">string array of orders to listen for</param>
        public void AddOrders(params string[] orders)
        {
            choices.Add(orders);
            if (isRunning)
            {
                isRunning = false;
                workingThread.Abort(); // force exit current thread
                Start();
            }

        }

        /// <summary>
        /// Start listening for commands
        /// </summary>
        public void Start()
        {
            if (isRunning)
                return; 

            isRunning = true;
            workingThread = new Thread(Record) { IsBackground = true };
            workingThread.Start();
        }

        void Record()
        {
            KinectAudioSource source = kinectSensor.AudioSource;

            Func<RecognizerInfo, bool> matchingFunc = r =>
            {
                string value;
                r.AdditionalInfo.TryGetValue("Kinect", out value);
                return
                    "True".Equals(value, StringComparison.InvariantCultureIgnoreCase) &&
                    "en-US".Equals(r.Culture.Name, StringComparison.InvariantCultureIgnoreCase);
            };

            var recognizerInfo = SpeechRecognitionEngine.InstalledRecognizers().Where(matchingFunc).FirstOrDefault();

            if (recognizerInfo == null)
                return;

            speechRecognitionEngine = new SpeechRecognitionEngine(recognizerInfo.Id);

            var gb = new GrammarBuilder { Culture = recognizerInfo.Culture };
            gb.Append(choices);
            
            var grammar = new Grammar(gb);

            speechRecognitionEngine.LoadGrammar(grammar);

            source.AutomaticGainControlEnabled = false;
            source.BeamAngleMode = BeamAngleMode.Adaptive;

            using (Stream sourceStream = source.Start())
            {
                speechRecognitionEngine.SetInputToAudioStream(sourceStream,
                    new SpeechAudioFormatInfo(EncodingFormat.Pcm, 16000, 16, 1, 32000, 2, null));

                while (isRunning)
                {
                    RecognitionResult result = speechRecognitionEngine.Recognize();
                    if (result != null && OrderDetected != null && result.Confidence > 0.7)
                        OrderDetected(result.Text);
                }
            }
        }

        /// <summary>
        /// Stops and discards this insance
        /// </summary>
        public void Stop()
        {
            isRunning = false;

            if (speechRecognitionEngine != null)
            {
                speechRecognitionEngine.Dispose();
            }
        }
    }
}
